/* 
Scenic Graphics Library
Copyright (C) 2007 Jouni Tulkki

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USApackage scenic;
*/

#include "Debug.h"
#include "scenic_Tesselator.h"
#include "Tesselator.h"

#include "common.h"
#include "PlainPolygonRenderer.h"
#include "AAPolygonRenderer.h"
#include "HQAAPolygonRenderer.h"

using namespace Scenic::Renderer;

/*
class JavaTesselator : public Tesselator
{
	JNIEnv *env;
	jobject obj;
	jclass clazz;
	jmethodID addVertexMethod;
	jmethodID addTriangleMethod;
public:
	JavaTesselator(JNIEnv *env, jobject obj);
	~JavaTesselator();

	void addVertex(double x, double y);
	void addTriangle(int vertex1, bool edge1, int vertex2, bool edge2, int vertex3, bool edge3);
};

JavaTesselator::JavaTesselator(JNIEnv *env, jobject obj)
{
	this->env = env;
	this->obj = env->NewGlobalRef(obj);
	clazz = env->GetObjectClass(this->obj);
	addVertexMethod = env->GetMethodID(clazz, "addVertex", "(DD)V");
	addTriangleMethod = env->GetMethodID(clazz, "addTriangle", "(IZIZIZ)V");
}

JavaTesselator::~JavaTesselator()
{
	env->DeleteGlobalRef(obj);
}

void JavaTesselator::addVertex(double x, double y)
{
	env->CallVoidMethod(obj, addVertexMethod, x, y);
}

void JavaTesselator::addTriangle(int vertex1, bool edge1, int vertex2, bool edge2, int vertex3, bool edge3)
{
	env->CallVoidMethod(obj, addTriangleMethod, 
		vertex1, edge1 ? JNI_TRUE : JNI_FALSE,
		vertex2, edge2 ? JNI_TRUE : JNI_FALSE,
		vertex3, edge3 ? JNI_TRUE : JNI_FALSE);
}
*/
struct Triangle
{
	int vertex1;
	bool edge1;
	int vertex2;
	bool edge2;
	int vertex3;
	bool edge3;
};

class TriangleBuffer : public Resource, public TriangleSink
{
	DynArray<Double2> points;
	DynArray<Triangle> triangles;
public:
	TriangleBuffer();
	~TriangleBuffer();

	void begin();
	void end();
	void addVertex(double x, double y);
	void addTriangle(int vertex1, bool edge1, int vertex2, bool edge2, int vertex3, bool edge3);

	void render(TriangleSink *sink);

	static TriangleBuffer* get(int id);
};


ResourcePool triangleBufferPool("TriangleBuffer");

TriangleBuffer::TriangleBuffer()
: Resource(&triangleBufferPool)
{
}

TriangleBuffer* TriangleBuffer::get(int id)
{
	return (TriangleBuffer*)triangleBufferPool.get(id);
}

TriangleBuffer::~TriangleBuffer()
{
}

void TriangleBuffer::begin()
{
	points.size = 0;
	triangles.size = 0;
}

void TriangleBuffer::end()
{
}

void TriangleBuffer::render(TriangleSink *sink)
{
	int i;

	// Begin
	sink->begin();

	// Add vertices
	for(i = 0; i < points.size; i++)
	{
		sink->addVertex(points[i].x, points[i].y);
	}

	// Add triangles
	for(i = 0; i < triangles.size; i++)
	{
		Triangle *t = &triangles[i];
		sink->addTriangle(t->vertex1, t->edge1, t->vertex2, 
			t->edge2, t->vertex3, t->edge3);
	}

	// End
	sink->end();
}

void TriangleBuffer::addVertex(double x, double y)
{
	Double2 p;

	p.x = x;
	p.y = y;

	points.add(p);
}

void TriangleBuffer::addTriangle(int vertex1, bool edge1, int vertex2, bool edge2, int vertex3, bool edge3)
{
	Triangle t;

	t.vertex1 = vertex1;
	t.edge1 = edge1;
	t.vertex2 = vertex2;
	t.edge2 = edge2;
	t.vertex3 = vertex3;
	t.edge3 = edge3;

	triangles.add(t);
}

static Tesselator *gluTesselator = new GLUTesselator();
static Tesselator *convexTesselator = new ConvexTesselator();
static Tesselator *tesselator;
static TriangleSink *sink;
static TriangleBuffer *oldTriangleBuffer;

JNIEXPORT jint JNICALL Java_scenic_Tesselator_begin
  (JNIEnv *env, jobject obj, jboolean isConvex, jint contextId)
{
	ENTER(java_scenic_Tesselator_begin);

	Context *context = getContext(contextId);

	if(isConvex)
		tesselator = convexTesselator;
	else
		tesselator = gluTesselator;

	TriangleBuffer *tb;
	
	if(oldTriangleBuffer)
	{
		tb = oldTriangleBuffer;
		oldTriangleBuffer = 0;
	}
	else
	{
		tb = new TriangleBuffer();
	}

	tesselator->begin(tb);
	return tb->id;
}

JNIEXPORT void JNICALL Java_scenic_Tesselator_free
  (JNIEnv *, jobject, jint id)
{
	ENTER(Java_scenic_Tesselator_free);

	TriangleBuffer *tb = TriangleBuffer::get(id);

	if(!oldTriangleBuffer)
		oldTriangleBuffer = tb;
	else
		delete tb;
}

JNIEXPORT void JNICALL Java_scenic_Tesselator_end
  (JNIEnv *, jobject, jint id)
{
	ENTER(Java_scenic_Tesselator_end);

	tesselator->end();
//	delete tesselator;
//	delete sink;
}

JNIEXPORT void JNICALL Java_scenic_Tesselator_beginContour
  (JNIEnv *, jobject, jint id)
{
	ENTER(java_scenic_Tesselator_beginContour);

	tesselator->beginContour();
}

JNIEXPORT void JNICALL Java_scenic_Tesselator_endContour
  (JNIEnv *, jobject, jint id)
{
	ENTER(Java_scenic_Tesselator_endContour);

	tesselator->endContour();
}

JNIEXPORT void JNICALL Java_scenic_Tesselator_vertex
  (JNIEnv *, jobject, jint id, jdouble x, jdouble y)
{
	ENTER(Java_scenic_Tesselator_vertex);

	tesselator->vertex(x, y);
}

JNIEXPORT void JNICALL Java_scenic_Tesselator_draw
  (JNIEnv *, jobject, jint id, jint contextId)
{
	ENTER(Java_scenic_Tesselator_draw);

	TriangleBuffer *t = (TriangleBuffer*)TriangleBuffer::get(id);
	Context *context = getContext(contextId);
	TriangleSink *sink;

#ifdef DIRECTX
	if(context->filterKernel)
		sink = new HQAAPolygonRenderer(context);
	else 
#endif
	if(context->polygonAntialias)
		sink = new AAPolygonRenderer(context);
	else
		sink = new PlainPolygonRenderer(context);

	t->render(sink);

	delete sink;
}
